/*
 * Copyright (c) 2009 John Pritchard, WTKX Project Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wtkx.ps;

import com.google.protobuf.Message;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * A kind of {@link wtkx.Object}, a proto streamer client can be
 * embedded into the scene graph with namespace {@code
 * http://wtkx.org/1.0/proto}.
 * 
 * The application would implement {@link Consumer}.
 */
public class Client
    extends wtkx.Object
    implements ps.Consumer
{
    public final static ps.Mux Mux = new ps.Mux();


    protected volatile URL src;
    protected volatile Consumer consumer;

    private volatile int entries;
    private volatile ps.Reader reader;
    private volatile ps.Writer writer;


    public Client(){
        super();
    }


    public final boolean isConnected(){
        return (2 == this.entries);
    }
    public final boolean isNotConnected(){
        return (2 != this.entries);
    }
    public final URL getSrc(){
        return this.src;
    }
    public final void setSrc(URL src){
        if ("http".equalsIgnoreCase(src.getProtocol())){
            this.src = src;
            this.stop();
        }
        else
            throw new IllegalArgumentException(src.toExternalForm());
    }
    public final void setSrc(String string){
        try {
            this.setSrc(new URL(string));
        }
        catch (MalformedURLException exc){
            throw new IllegalArgumentException(string,exc);
        }
    }
    public final Consumer getConsumer(){
        return this.consumer;
    }
    public final void setConsumer(Consumer c){
        this.consumer = c;
    }
    public final void destroy(){
        try {
            this.stop();
        }
        finally {
            this.consumer = null;
        }
    }
    public void init(wtkx.Window w, wtkx.Application a){
        Mux.register(Wtkx.class);
        if (a instanceof Consumer)
            this.consumer = (Consumer)a;
        else if (w instanceof Consumer)
            this.consumer = (Consumer)w;
        try {
            this.start();
        }
        catch (java.net.ProtocolException exc){
            exc.printStackTrace();
        }
    }
    public final void start()
        throws java.net.ProtocolException
    {
        if (null != this.consumer){
            ps.Writer writer = this.writer;
            if (null == writer){
                writer = new ps.Writer(this.src,this);
                this.writer = writer;
                writer.start();
            }
            ps.Reader reader = this.reader;
            if (null == reader){
                reader = new ps.Reader(this.src,this);
                this.reader = reader;
                reader.start();
            }
        }
        else
            throw new IllegalStateException("Missing consumer");
    }
    public final void stop(){
        try {
            ps.Writer writer = this.writer;
            if (null != writer){
                this.writer = null;
                writer.stop();
            }
        }
        finally {
            ps.Reader reader = this.reader;
            if (null != reader){
                this.reader = null;
                reader.stop();
            }
        }
    }
    public final void update(Message m){
        Consumer consumer = this.consumer;
        if (null != consumer)
            consumer.clientUpdate(this,m);
    }
    public final void deliver(Message m){
        ps.Writer writer = this.writer;
        if (null != writer)
            writer.deliver(m);
        else
            throw new IllegalStateException("Not started");
    }
    public final ps.Mux clientMux(){
        return Mux;
    }
    public final void clientEnter(ps.Client client){
        this.entries++;
        if (2 == this.entries)
            this.consumer.clientConnected(this);
    }
    public final void clientExit(ps.Client client){
        this.entries--;
        if (0 == this.entries)
            this.consumer.clientDisconnected(this);
        /*
         * In order to correctly cover all cases, this must occur
         * without condition.  Looks less than great, but it's
         * correct.
         */
        Stop stop = new Stop(this);
    }
}
